<?php
/**
 * Created by PhpStorm.
 * User: xiaomeng
 * Date: 17-7-13
 * Time: 上午10:06
 */
namespace mphp\Socket;
use mphp\Common\Utils;
use mphp\Core\Config;
use mphp\Core\Db;
use mphp\Core\syncDb;
class Message
{
    private static $cmd = ['login','sendmsg'];
    private static $sendmsg_allow_keys = ['cmd','target_type','to','msg_body'];
    private static $login_allow_keys = ['cmd','username'];
    private static $msg_txt_allow_keys = ['type','msg'];
    private static $msg_img_allow_keys = ['type','url','filename','secret','size'];
    private static $msg_type = ['txt','img','file'];
    private static $target_type = ['users','chatgroups','chatrooms'];
    private static $lastSentTime = array();

    /**
     * message:
     * cmd:string 指令
     * target_type:string   users 给用户发消息。chatgroups: 给群发消息，chatrooms: 给聊天室发消息
     * to:
     * msg_body:array 消息内容
     * from:string 表示消息发送者。无此字段Server会默认设置为"from":"admin"，有from字段但值为空串("")时请求失败
     * @param $server
     * @param $frame
     */
    public static function handleMessage($server, $frame)
    {
        $now = self::getMillisecond();
        $fd = $frame->fd;
        $config = Config::get('chat');
        //1.上一次发送的时间超过了允许的值，每N秒可以发送一次
        if(isset(self::$lastSentTime[$fd]) && $now - self::$lastSentTime[$fd] < $config['send_interval_limit'])
        {
            self::sendErrorMessage($server,$fd,-2,"发送消息太频繁,等待{$config['send_interval_limit']}ms后再发送");
            return false;
        }
        self::$lastSentTime[$fd] = $now;
        $data = json_decode($frame->data,true);
        //2.验证指令
        if(!isset($data['cmd']) || !in_array($data['cmd'],self::$cmd))
        {
            self::sendErrorMessage($server,$fd,-1,'指令不正确');
            return false;
        }
        //3.过滤数据
        $data = self::filterMsg($data);
        $data = Utils::escape($data,false);
        //4.验证数据
        $res = self::validateMsg($data);
        if($res !== true)
        {
            $res = implode(',',$res);
            self::sendErrorMessage($server,$fd,-3,"缺少必须的参数{$res}");
            return false;
        }

        //5.具体处理
        $func = 'cmd_'.$data['cmd'];
        self::$func($server,$fd,$data,$config);

    }

    /**
     * @param $server
     * @param $fd
     */
    public static function cmd_login($server,$fd,$data,$config)
    {
        $online = syncDb::redis()->get('chat_online');
        if(empty($online))
        {
            $online = [];
        }
        else
        {
            $online = json_decode($online,true);
        }
        $online[$data['username']] = $fd;
        syncDb::redis()->set('chat_online',json_encode($online,JSON_UNESCAPED_UNICODE));

        //消息接受成功是否应答
        if($config['receive_replay'] === true)
        {
            self::sendSuccessMessage($server,$fd);
        }
        //发送离线消息
        $offmsgs = syncDb::collection('chat_offline_msg')->where(['member'=>$data['username']])->field('msg')->get();
        foreach($offmsgs as $msg)
        {
            $server->push($fd,$msg['msg']);
        }
        syncDb::collection('chat_offline_msg')->where(['member'=>$data['username']])->delete();
    }

    /**
     * @param $server
     * @param $fd
     */
    public static function cmd_sendmsg($server,$fd,$data,$config)
    {
        $target_type = $data['target_type'];
        if(!in_array($target_type,self::$target_type))
        {
            $res = implode(',',self::$target_type);
            self::sendErrorMessage($server,$fd,-5,"允许的目标类型{$res}");
            return false;
        }

        $type = $data['msg_body']['type'];
        if(!in_array($type,self::$msg_type))
        {
            $res = implode(',',self::$msg_type);
            self::sendErrorMessage($server,$fd,-5,"允许的消息类型{$res}");
            return false;
        }

        //验证数据
        $res = self::validateMsgType($data['msg_body']);
        if($res !== true)
        {
            $res = implode(',',$res);
            self::sendErrorMessage($server,$fd,-4,"消息内容缺少必须的参数{$res}");
            return false;
        }
        //文字消息检测字数
        if($type == 'txt' && isset($config['message_max_len']) && mb_strlen($data['msg_body']['msg'],'UTF-8') > $config['message_max_len'])
        {
            self::sendErrorMessage($server,$fd,-6,"允许的字符最大长度为{$config['message_max_len']}");
            return false;
        }
        //消息接受成功是否应答
        if($config['receive_replay'] === true)
        {
            self::sendSuccessMessage($server,$fd);
        }

        //task
        //发送消息储存消息
        $data['fd'] = $fd;
        $data = json_encode($data,JSON_UNESCAPED_UNICODE);
        $server->task($data);
    }


    /**
     * 发送错误信息
     * @param $server
     * @param $fd
     * @param $code
     * @param $msg
     */
    public static function sendErrorMessage($server,$fd, $code, $msg)
    {
        $msg = json_encode(array('cmd' => 'error', 'code' => $code, 'msg' => $msg),JSON_UNESCAPED_UNICODE);
        $server->push($fd, $msg);
    }

    /**
     * 发送成功信息
     * @param $server
     * @param $fd
     * @param $code
     * @param $msg
     */
    public static function sendSuccessMessage($server,$fd)
    {
        $msg = json_encode(array('cmd' => 'success', 'code' => 0, 'msg' => '成功'),JSON_UNESCAPED_UNICODE);
        $server->push($fd, $msg);
    }
    /**
     * 如果为拒绝模式，所有不在过滤参数$params中的键值都将被删除
     *
     */
    public static function filterMsg($params,$mode = 'deny')
    {
        $cmd = $params['cmd'];
        $ak = $cmd.'_allow_keys';

        if ($mode == 'deny')
        {
            $allow_keys = self::$$ak;
            $allow = array_values($allow_keys);
            $have = array_keys($params);
            foreach ($have as $ha)
            {
                if (!in_array($ha, $allow))
                {
                    unset($params[$ha]);
                }
            }
        }
        return $params;
    }

    /**
     * 验证允许的键与传多来的是否一致
     * @param $params
     * @return mixed
     */
    public static function validateMsg($params)
    {
        $cmd = $params['cmd'];
        $ak = $cmd.'_allow_keys';
        $allow_keys = self::$$ak;
        $have = array_keys($params);
        $res = array_diff($allow_keys,$have);
        if(!empty($res))
        {
            return $res;
        }
        return true;

    }

    /**
     * 验证允许的键与传多来的是否一致
     * @param $params
     * @return mixed
     */
    public static function validateMsgType($params)
    {
        $type = $params['type'];//$msg_txt_allow_keys
        $ak = 'msg_'.$type.'_allow_keys';
        $allow_keys = self::$$ak;
        $have = array_keys($params);
        $res = array_diff($allow_keys,$have);
        if(!empty($res))
        {
            return $res;
        }
        return true;

    }

    public static function getMillisecond() {
        list($s1, $s2) = explode(' ', microtime());
        return sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000);
    }
}